/*
 * RequestManager.java
 *
 * Created on 2008/08/21 +0900
 *
 * $Revision: 58 $
 * $Date: 2008-12-06 08:59:45 +0000 (Sat, 06 Dec 2008) $
 */

package org.gikolet.framework.request;

import java.util.Enumeration;

import org.gikolet.framework.util.Assert;
import org.gikolet.framework.util.Deque;

/**
 * @author tetsutaro (latest modification by $Author: tetsutaro.ss $)
 * @version $Revision: 58 $ $Date: 2008-12-06 08:59:45 +0000 (Sat, 06 Dec 2008) $
 */
public class RequestManager {
    private RequestProcesserProvider provider;

    private Deque queue;

    public RequestManager(final RequestProcesser processer) {
	this(new RequestProcesserProvider() {
	    public RequestProcesser getRequestProcesser() {
		return processer;
	    }
	});
    }

    public RequestManager(RequestProcesserProvider provider) {
	Assert.isNotNull(provider);

	this.provider = provider;
	this.queue = new Deque();
    }

    public Enumeration tickets() {
	return this.queue.elements();
    }

    public RequestTicket getTicketAt(int index) {
	return (RequestTicket) this.queue.element(index);
    }

    public RequestTicket invoke(Request request) {
	final RequestTicketImpl ticket = new RequestTicketImpl(request);

	this.provider.getRequestProcesser().invoke(ticket);

	return ticket;
    }

    public void invokeAndWait(Request request) throws InterruptedException, Throwable {
	RequestTicketImpl ticket = new RequestTicketImpl(request, Thread.currentThread());

	try {
	    this.provider.getRequestProcesser().invokeAndWait(ticket);

	} catch (InterruptedException e) {
	    ticket.interrupted();
	    throw e;
	}
    }

    class RequestTicketImpl extends ProxyRequest implements RequestTicket {
	private Thread thread;

	RequestTicketImpl(Request request) {
	    this(request, null);
	}

	RequestTicketImpl(Request request, Thread thread) {
	    super(request, false);

	    this.thread = thread;
	    RequestManager.this.queue.pushQueue(this);
	}

	public synchronized void interrupted() {
	    this.thread = null;
	}

	public synchronized void execute() {
	    try {
		super.execute();
	    } finally {
		RequestManager.this.queue.popFirst();
	    }
	}

	public synchronized void cancel() {
	    setCancel(true);

	    RequestManager.this.queue.popFirst();

	    if (this.thread != null) {
		this.thread.interrupt();
	    }
	}
    }
}
